package bll

import (
	"context"
	"fmt"
	log "ota/collector/logger"
	"ota/event"
	"ota/model"
	"ota/utils"
	"ota/utils/cron"
	"sync"
	"time"
)

type timeoutTimer struct {
	*cron.Cron
	locker     sync.Mutex
	id2EntryId map[int64]cron.EntryID
}

var (
	userUpgradeTimeoutTimer = &timeoutTimer{
		locker:     sync.Mutex{},
		Cron:       cron.New(cron.WithLocation(time.Local), cron.WithSeconds()),
		id2EntryId: make(map[int64]cron.EntryID),
	}
)

func init() {
	userUpgradeTimeoutTimer.init()
}

func (a *timeoutTimer) init() func() {

	a.Start()
	return func() {}
}

func (a *timeoutTimer) onEvent(evtData *event.UserUpgradeEventData) {

	if evtData.TimeoutDuration < 0 {
		return
	}
	var (
		now       = time.Now().Unix()
		startTime = time.Unix(now+evtData.TimeoutDuration, 0)
	)

	spec := fmt.Sprintf("%v %v %v %v %v *", startTime.Second(), startTime.Minute(), startTime.Hour(), startTime.Day(), int(startTime.Month()))

	if err := a.create(spec, evtData); err != nil {
		log.Errorf("create upgrading Timer error:%+v", err)
		return
	}

	log.Infof("Ota upgradingTimer onEvent(%v,spec:%v start_time:%v)", spec, startTime, startTime.String())
}

func (a *timeoutTimer) create(spec string, evtData *event.UserUpgradeEventData) error {

	a.locker.Lock()
	defer a.locker.Unlock()

	var (
		err     error
		entryId cron.EntryID
	)

	if _, ok := a.id2EntryId[evtData.Id]; !ok {
		entryId, err = a.AddFunc(spec, a.timerCallBack, evtData)
		if err != nil {
			return err
		}
		a.id2EntryId[evtData.Id] = entryId
	}

	return err
}

func (a *timeoutTimer) delete(evtData *event.UserUpgradeEventData) error {

	a.locker.Lock()
	defer a.locker.Unlock()
	var (
		err  error
		data = evtData
	)

	if entryId, ok := a.id2EntryId[data.Id]; ok {
		a.Remove(entryId)
		delete(a.id2EntryId, data.Id)
	}

	return err
}

func (a *timeoutTimer) timerCallBack(args ...interface{}) {

	var (
		data       = args[1].(*event.UserUpgradeEventData)
		ctx, _     = context.WithTimeout(context.Background(), time.Minute*3)
		now        = time.Now().Unix()
		timeFormat = "2006-01-02 15:04:05"
	)

	defer func() {
		_ = a.delete(&event.UserUpgradeEventData{Id: data.Id})
	}()

	log.Infof("ota task:%v %v timout duration:%v has expired,set failure status", data.Id, time.Unix(data.StartTime, 0).Format(timeFormat), data.TimeoutDuration)
	_, _ = Ota.updateUserUpgrade(ctx, &model.UserUpgradeUpdateRequest{
		Id:                utils.GetValuePointer(data.Id),
		PlanId:            utils.GetValuePointer(data.PlanId),
		Status:            utils.GetValuePointer(int(model.UpgradeResultFailure)),
		FailureLatestTime: utils.GetValuePointer(now),
		FailureReason: utils.GetValuePointer(fmt.Sprintf("超时失败,任务开始时间:%v,超时时长:%v,超时时间:%v",
			time.Unix(data.StartTime, 0).Format(timeFormat),
			data.TimeoutDuration, time.Unix(now, 0).Format(timeFormat))),
	})
}
